/*
 *  hook_main.cpp
 *  bbinj
 *
 *  Created by msftguy on 6/16/10.
 *  Copyright 2010 __MyCompanyName__. All rights reserved.
 *
 */

#include "hook_main.h"

#include <unistd.h>
#include <dlfcn.h>
#include <mach-o/dyld.h>

#include "substrate.h"
#include "impHookApi.h"

#include "logging.h"
#include "bb_ops.h"
#include "safe.h"
#include "fuzz.h"

static ssize_t my_read(int fd, void* buf, size_t size) {
	int result = read(fd, buf, size);
	log_io(fd, "_read", buf, result > 0 ? result : 0, result);
	return result;
}

static ssize_t my_write(int fd, const void* buf, size_t size) {
	int result = write(fd, buf, size);
	log_io(fd, "_write", buf, size, result);
	return result;
}

void hook_blacksn0w_io()
{
	void* hBS = dlopen("/usr/lib/blacksn0w.dylib", RTLD_GLOBAL);
	NSLog(@"dlopen(blacksnow) = %p", hBS);

	const mach_header* mh = NULL;

	for (int i = 0; i < _dyld_image_count(); ++i) {
		if (NULL != strcasestr(_dyld_get_image_name(i), "blacksn0w")) {
			mh = _dyld_get_image_header(i);
			NSLog(@"blacksn0w module found at %p", mh);
			break;
		}
	}
	if (mh == NULL) {
		NSLog(@"blacksn0w module not found!!!");
		return;
	}
	
	uintptr_t* pReadImp = get_import_ptr(mh, "_read");
	uintptr_t* pWriteImp = get_import_ptr(mh, "_write");
	*pReadImp = (uintptr_t)my_read;
	*pWriteImp = (uintptr_t)my_write;
	NSLog(@"Blacksn0w IO hooked: r=%p w=%p", pReadImp, pWriteImp);
}

typedef int (*at_cmd_handler_t) (int arg_r0, int arg_r1, int arg_r2, int arg_r3,
								  int stkarg_1, int stkarg_2, char* stkarg_3_cmd, int stkarg_4, int stkarg_5, int stkarg_6);

at_cmd_handler_t at_cmd_handler_call_orig;

static uint32_t g_pingResponseXref = 0;

static bool g_payloadFailed = false; 

int my_C_at_cmd_handler(int arg_r0, int arg_r1, int arg_r2, int arg_r3,
						int stkarg_1, int stkarg_2, char* stkarg_3_cmd, int stkarg_4, int stkarg_5, int stkarg_6)
{
	uint32_t caller = (uint32_t) __builtin_return_address(1);
	const char* cmd = stkarg_3_cmd == NULL ? "<NULL>" : stkarg_3_cmd;
	log_at(cmd);
	
	if (!g_payloadFailed && strcmp(cmd, "e0") == 0 && (g_pingResponseXref - caller < 0x80)) {
		safe_reportStart();
		if (bb_exploit_main()) {
			safe_reportStartSuccess();
		} else {
			g_payloadFailed = true;
		}
		return 0;
	}
	return at_cmd_handler_call_orig(arg_r0, arg_r1, arg_r2, arg_r3,
							 stkarg_1, stkarg_2, stkarg_3_cmd, stkarg_4, stkarg_5, stkarg_6);
}

extern "C" void my_at_cmd_handler();

int my_SecKeyRawVerify()
{
	return errSecSuccess;
}

void setup_hooks()
{
	void* pAtHandler = NULL;
	if (find_at_handler(&pAtHandler, (void**)&g_pingResponseXref)) {
		log_progress("find_at_handler: %p; %p", pAtHandler, g_pingResponseXref);
		at_cmd_handler_call_orig = *(at_cmd_handler_t*)pAtHandler;
		patch_word(pAtHandler, (void*)my_C_at_cmd_handler);
	}
//TODO: tethering patch
//	const mach_header* mh = _dyld_get_image_header(0);
//	uintptr_t* pSecKeyRawVerify = get_import_ptr(mh, "_SecKeyRawVerify");
//	if (pSecKeyRawVerify != NULL) {
//		*pSecKeyRawVerify = (uintptr_t)my_SecKeyRawVerify;
//	}
	
}